package hud;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.MongoIterable;
import data.DatabaseClient;
import event.EventBase;
import event.EventEnum;
import event.EventSystem;
import event.ListenerInterface;
import hud.data.CollectionInformation;
import hud.data.DatabaseTreeItem;
import hud.data.DatabaseTreeItemType;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import org.bson.Document;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DatabaseTreeView implements ListenerInterface {
    private static DatabaseTreeView instance;
    private TreeItem rootItem;
    private TreeView treeView;
    private DatabaseTreeView() {

    }

    public static DatabaseTreeView instance() {
        if (instance == null) {
            instance = new DatabaseTreeView();
        }
        return instance;
    }

    public VBox init(VBox viewBox) {
        VBox leftVBox = new VBox();
        leftVBox.setPadding(new Insets(0));
        leftVBox.setSpacing(0);
        leftVBox.prefHeightProperty().bind(viewBox.heightProperty());

        rootItem = new TreeItem("root");

        treeView = new TreeView(rootItem);
        treeView.setShowRoot(false);
        treeView.prefHeightProperty().bind(leftVBox.heightProperty());
        treeView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                if (event.getClickCount() == 2) {
                    TreeItem item = (TreeItem) treeView.getSelectionModel().getSelectedItem();
                    DatabaseTreeItem databaseTreeItem = (DatabaseTreeItem) item.getValue();

                    // Collection node been double-clicked
                    if (databaseTreeItem.getType() == DatabaseTreeItemType.COLLECTIONNODE) {
                        // 1, to avoid duplication, we clear old
                        item.getChildren().clear();

                        // 2, add new
                        // database
                        //   --Collections   --> databaseTreeItem pointer
                        TreeItem treeItem = item.getParent();
                        DatabaseTreeItem databaseTreeItemValue = (DatabaseTreeItem) treeItem.getValue();
                        String databaseName = databaseTreeItemValue.getName();

                        MongoDatabase mongoDatabase = databaseTreeItem.getClient().getClient().getDatabase(databaseName);
                        MongoIterable<String> collections = mongoDatabase.listCollectionNames();
                        MongoCursor<String> cursor = collections.iterator();

                        List<String> collectionList = new ArrayList<>();
                        while(cursor.hasNext()) {
                            collectionList.add(cursor.next());
                        }
                        cursor.close();

                        Collections.sort(collectionList);
                        for (int i = 0; i < collectionList.size(); ++i) {
                            DatabaseTreeItem collectionItem = new DatabaseTreeItem(collectionList.get(i), DatabaseTreeItemType.COLLECTION, databaseTreeItem.getClient());
                            item.getChildren().add(new TreeItem(collectionItem));
                        }
                        item.setExpanded(true);
                    }
                    // Collection been double-clicked
                    else if (databaseTreeItem.getType() == DatabaseTreeItemType.COLLECTION) {
                        EventSystem.instance().dispatch(new EventBase(EventEnum.openCollection, item));
                    }
                }
            }
        });
        EventHandler<MouseEvent> mouseEventHandle = (MouseEvent event) -> {
            handleMouseClicked(event);
        };
        treeView.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEventHandle);

        leftVBox.getChildren().add(treeView);

        prepareListenEvents();

        return leftVBox;
    }

    private void prepareListenEvents() {
        EventSystem.instance().on(EventEnum.openCollection, this);
        EventSystem.instance().on(EventEnum.mongoClientAdd, this);
    }

    private void handleMouseClicked(MouseEvent event) {
        Node node = event.getPickResult().getIntersectedNode();
        if (node instanceof Text || (node instanceof TreeCell && ((TreeCell) node).getText() != null)) {
            TreeItem item = (TreeItem) treeView.getSelectionModel().getSelectedItem();
            DatabaseTreeItem databaseTreeItem = (DatabaseTreeItem) item.getValue();

            if (event.getButton().equals(MouseButton.SECONDARY)) {
                ContextMenu contextMenu = ContextMenuMan.getInstance().getDatabaseContexMenu();
                contextMenu.show(node, event.getScreenX(), event.getScreenY());
            }
        }
    }

    @Override
    public void process(EventBase event) {
        if (event.getName().equals(EventEnum.mongoClientAdd)) {
            DatabaseClient databaseClient = (DatabaseClient) event.getBody();
            DatabaseTreeItem databaseTreeViewItem = new DatabaseTreeItem(databaseClient.getName(), DatabaseTreeItemType.CLIENT, databaseClient);
            TreeItem clientItem = new TreeItem(databaseTreeViewItem);

            MongoCursor<String> dbNames = databaseClient.getClient().listDatabaseNames().iterator();
            while (dbNames.hasNext()) {
                DatabaseTreeItem databaseItem = new DatabaseTreeItem(dbNames.next(), DatabaseTreeItemType.DATABASE, databaseClient);
                TreeItem databaseTreeItem = new TreeItem(databaseItem);
                DatabaseTreeItem collectionItem = new DatabaseTreeItem("Collections", DatabaseTreeItemType.COLLECTIONNODE, databaseClient);
                databaseTreeItem.getChildren().add(new TreeItem(collectionItem));
                clientItem.getChildren().add(databaseTreeItem);
            }
            rootItem.getChildren().add(clientItem);
            clientItem.setExpanded(true);
        }
        else if (event.getName().equals(EventEnum.openCollection)) {
            TreeItem item = (TreeItem) event.getBody();
            TreeItem databaseItem = item.getParent().getParent();
            String databaseName = ((DatabaseTreeItem) databaseItem.getValue()).getName();

            DatabaseTreeItem collectionItem = (DatabaseTreeItem) item.getValue();

            if (collectionItem.getType() == DatabaseTreeItemType.COLLECTION) {
                MongoCollection<Document> collection = collectionItem.getClient().getClient().getDatabase(databaseName).getCollection(collectionItem.getName());
                String clientName = collectionItem.getClient().getName();
                CollectionInformation collectionInformation = new CollectionInformation();
                collectionInformation.setClientName(clientName);
                collectionInformation.setHostName(collectionItem.getClient().getHost());
                collectionInformation.setMongoCollection(collection);
                collectionInformation.setPort(collectionItem.getClient().getPort());
                CollectionView.instance().newTab(collectionInformation);
            }
        }
    }
}
